package com.fansl.allround.auth.handler;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import com.fansl.allround.common.core.constant.CommonConstants;
import com.fansl.allround.common.security.util.AuthUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.Assert;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author fansl
 * @Description: 认证成功返回token
 * @date 2019/9/27 18:30
 */
@Slf4j
@AllArgsConstructor
@Builder
public class AllroundAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private static final String BASIC_ = "Basic ";
    private ObjectMapper objectMapper;
    private PasswordEncoder passwordEncoder;
    private ClientDetailsService clientDetailsService;
    private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;

    /**
     * 调用spring security oauth API 生成 oAuth2AccessToken
     *
     * @param request        请求
     * @param response       响应
     * @param authentication 认证方式
     * @throws IOException      IO异常
     * @throws ServletException Servlet异常
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        String header = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (header == null || !header.startsWith(BASIC_)) {
            throw new UnapprovedClientAuthenticationException("请求头中client信息为空");
        }
        try {
            String[] tokens = AuthUtils.extractAndDecodeHeader(header);
            Assert.notNull(tokens.length == 2, "client 信息错误");
            String clientId = tokens[0];
            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
            //校验secret
            if (!passwordEncoder.matches(tokens[1], clientDetails.getClientSecret())) {
                throw new InvalidClientException("Given client ID does not match authenticated client");
            }
            String grantType = request.getParameter("grant_type");
            String scope = request.getParameter("scope");
            TokenRequest tokenRequest =
                    new TokenRequest(MapUtil.newHashMap(), clientId, Sets.newHashSet(scope), grantType);
            //校验scope
            new DefaultOAuth2RequestValidator().validateScope(tokenRequest, clientDetails);

            OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

            OAuth2Authentication oAuth2Authentication =
                    new OAuth2Authentication(oAuth2Request, authentication);
            OAuth2AccessToken auth2AccessToken =
                    defaultAuthorizationServerTokenServices.createAccessToken(oAuth2Authentication);
            log.info("获取token成功：{}", auth2AccessToken.getValue());

            response.setCharacterEncoding(CharsetUtil.UTF_8);
            response.setContentType(CommonConstants.CONTENT_TYPE);
            PrintWriter printWriter = response.getWriter();
            printWriter.append(objectMapper.writeValueAsString(auth2AccessToken));
        } catch (IOException e) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }

    }
}
